/******************************************************************************************************
 *  The skeletion b+tree table database for Tokyo Cabinet
 *  the b+tree table can support the field index(json format value) like tct(table engine).
 *  specifiction:
 *    MUST BE an B+ TREE Table ENGINE 
 *    the key and value MUST BE utf-8 format.
 *    addint, adddouble, putcat,putproc,putdup, putdupback is never indexed
 *    outlist use the tcbdbout instead of tcbdbout3!
 *    the setindex command is same as the tct, Note the setindex do not add the original data to the index unless u re-index it.
 *      remove the index, u can use the TDBITVOID(9999) option.
 *        tcrmgr misc localhost setindex key 9999
 *      ReIndex, u can use the TDBITREINDEX(10000) option.
 *        tcrmgr misc localhost setindex key 10000
 *    the query command is same as the tct[todo]
 *    tcadbcopy command is not re-indexed.[todo]
 *    Key Specifiction:
 *      the key length should be less 2047(MAXKEYLEN) chars.
 *      the duplication(same) key is not allowed.
 *      the start with "_" is NOT ALLOWED, we keep reserve as internal used.
 *      the "/", "|", "\", """, "." is not allowed too. u need escape these chars.
 *      _DB/index/_conf/[FieldName]: 
 *         [IndexOptions]: the index options same as the tct's index options.[todo]
 *      _DB/index/[FieldName]/[FieldValue]/[KeyName]: the index put here, store value is the field of the key.
 *         the FieldValue will be quoted("") if it is a string.
 *         value: [KeyName]
 *    Structured-Value for Index Specifiction:
 *      STRICT JSON Object only 
 *      The first char MUST BE "{" and the last char MUST BE "}" 
 *      NO MORE BLANK CHARS BE ALLOWED
 *      the string MUST BE in double quote : ""
 *      all the key in the json object must be a string.
 *      only string, number, bool, null can be indexed.
 *      the "/", "|", "\", "_", """, "." char keep reserved. so the field value string MUST NOT USE these characters(u should escape these chars). 
 *   Structured-Value Store Specifiction:
 *      Spec.1:
 *        Store As Json String
 *        Key: [KeyName]
 *        Value: "{"[FieldName]":[FieldValue], ...}" : the json string
 *
#test :
tcrmgr vanish localhost 
tcrmgr misc localhost setindex key 0 
tcrmgr misc localhost setindex goodkey 0 
tcrmgr misc localhost setindex key1 0 
tcrmgr misc localhost setindex key2 0
tcrmgr list localhost 
tcrmgr put localhost mykey "{\"key\":\"hello\",\"goodkey\":false}"
tcrmgr put localhost mykey2 "{\"key\":\"你好\",\"goodkey\":true}"
tcrmgr list localhost 
 _DB/index/_conf/goodkey
 _DB/index/_conf/key
 _DB/index/_conf/key1
 _DB/index/_conf/key2
 _DB/index/goodkey/false/mykey
 _DB/index/goodkey/true/mykey2
 _DB/index/key/"hello"
 _DB/index/key/"你好"
 mykey
 mykey2
 tcrmgr misc localhost put mykey3 "{\"key\":\"hi,every body\",\"goodkey\":null, \"opt\":0, \"key2\":"same thing"}"

 //remove the key index
 tcrmgr misc localhost setindex key 9999
 *
 *    
 *                                          Copyright (C) 2009-2010 Riceball LEE(riceball.lee@gmail.com)
 ******************************************************************************************************/


#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <uuid/uuid.h>
#include <jansson.h>  //json 
//#include "tcjson.h"

#include "tcutil.h"
#include "tchdb.h"
#include "tcbdb.h"
#include "tcadb.h"
#include "tctdb.h"
#include "tcskelbdb.h"
#include "myconf.h"

#define TCNUMBUFSIZ    32                // size of a buffer for a number
#define MAXLISTSIZE    2//4096*16           //-1 means get all items at once.
//#define DEBUG

#define URN_TEMPLATE "urn:uid:"

#define BDBLOCKMETHOD(TC_bdb, TC_wr)                            \
((TC_bdb)->mmtx ? tcbdblockmethod((TC_bdb), (TC_wr)) : true)
#define BDBUNLOCKMETHOD(TC_bdb)                         \
((TC_bdb)->mmtx ? tcbdbunlockmethod(TC_bdb) : true)
#define BDBLOCKCACHE(TC_bdb)                            \
((TC_bdb)->mmtx ? tcbdblockcache(TC_bdb) : true)
#define BDBUNLOCKCACHE(TC_bdb)                          \
((TC_bdb)->mmtx ? tcbdbunlockcache(TC_bdb) : true)
#define BDBTHREADYIELD(TC_bdb)                          \
do { if((TC_bdb)->mmtx) sched_yield(); } while(false)

/* private function prototypes */
static bool tcbdblockmethod(TCBDB *bdb, bool wr);
static bool tcbdbunlockmethod(TCBDB *bdb);
static bool tcbdblockcache(TCBDB *bdb);
static bool tcbdbunlockcache(TCBDB *bdb);

static bool tcADBOpen(TCADB *adb, const char *name);
static bool tcADBput(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
static bool tcADBputkeep(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz);
//static bool tcMeDBputcat(TCADB *aDB, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
static bool tcADBOut(TCADB *aDB, const char *kbuf, int ksiz);
static TCLIST *tcADBmisc(TCADB *aDB, const char *name, const TCLIST *args);
//static bool tcMeDBputproc(TCADB *aDB, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
//                            TCPDPROC proc, void *op);
/* callback Put/PutKeep */
typedef bool (*TCADBPUTPROC)(TCADB *aDB, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
typedef bool (*TCBDBPUTPROC)(TCBDB *aDB, const void *kbuf, int ksiz, const void *vbuf, int vsiz);
typedef bool (*TCBDBOUTPROC)(TCBDB *aDB, const void *kbuf, int ksiz);

/* add/update Key/Structured-Value with index supports */
static bool tcADBReplace(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCADBPUTPROC aPutProc);
/* remove the Key with index supports */
static bool tcADBDelete(TCADB *aDB, const char *kbuf, int ksiz);
/* set an index to a field */
static bool tcBDBIndexSet(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt);
/* remove an index of a field */
static bool tcBDBIndexRemove(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz);
//static bool tcBDBIndexClear(TCBDB *aDB, const char *kbuf, int ksiz);

/* add/update Key/Structured-Value with index supports */
static bool tcBDBReplace(TCBDB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCBDBPUTPROC aPutProc);
/* remove the Key with index supports */
static bool tcBDBDelete(TCBDB *aDB, const char *kbuf, int ksiz, const TCBDBOUTPROC aOutProc);

/*************************************************************************************************
 * API
 *************************************************************************************************/
inline char *GenerateUID(void) {
  uuid_t uu;
  char char_uu[sizeof(URN_TEMPLATE) + 37];
  
  //strcpy (char_uu, URN_TEMPLATE);
  
  uuid_generate (uu);
  //uuid_unparse (uu, char_uu + sizeof (URN_TEMPLATE) - 1);
  uuid_unparse (uu, char_uu);
#if defined(DEBUG)
  fprintf (stderr, "uid:%s\n", char_uu);
#endif
  uuid_clear (uu);
  
  return (char_uu);  
}

inline bool isKeepReservedChar(const char *aStr) {
  return (*aStr == '_' || *aStr == '/' || *aStr == '.' || *aStr == '|' || *aStr == '\\' || *aStr == '"');
}

//str cat json value(string, number, bool, null)
inline void StrCatJson(char* aStr, const void* aJsonValue) {
  char numbuf[TCNUMBUFSIZ];
  switch (json_typeof((json_t*)aJsonValue)) {
    case JSON_STRING:
      strcat(aStr, "\"");
      strcat(aStr, json_string_value((json_t*)aJsonValue));
      strcat(aStr, "\"");
      break;
    case JSON_INTEGER:
      sprintf(numbuf, "%d", json_integer_value((json_t*)aJsonValue));
      strcat(aStr, numbuf);
      break;
    case JSON_REAL:
      sprintf(numbuf, "%f", json_real_value((json_t*)aJsonValue));
      strcat(aStr, numbuf);
    case JSON_TRUE:
      strcat(aStr, "true");
      break;
    case JSON_FALSE:
      strcat(aStr, "false");
      break;
    case JSON_NULL:
      strcat(aStr, "null");
      break;
    default:
      break;
  }
  
}

static void tcBDBSkelSet(ADBSKEL *skel){
#if defined(DEBUG)
  //fprintf(stderr, "%s\n", "tcBDBSkelSet");
#endif
  skel->opq = tcadbnew();
  skel->del = (void (*)(void *))tcadbdel;
  skel->open = (bool (*)(void *, const char *))tcADBOpen;
  skel->close = (bool (*)(void *))tcadbclose;
  skel->put = (bool (*)(void *, const void *, int, const void *, int))tcADBput;
  skel->putkeep = (bool (*)(void *, const void *, int, const void *, int))tcADBputkeep;
  skel->putcat = (bool (*)(void *, const void *, int, const void *, int))tcadbputcat;
  skel->out = (bool (*)(void *, const void *, int))tcADBOut;
  skel->get = (void *(*)(void *, const void *, int, int *))tcadbget;
  skel->vsiz = (int (*)(void *, const void *, int))tcadbvsiz;
  skel->iterinit = (bool (*)(void *))tcadbiterinit;
  skel->iternext = (void *(*)(void *, int *))tcadbiternext;
  skel->fwmkeys = (TCLIST *(*)(void *, const void *, int, int))tcadbfwmkeys;
  skel->addint = (int (*)(void *, const void *, int, int))tcadbaddint;
  skel->adddouble = (double (*)(void *, const void *, int, double))tcadbadddouble;
  skel->sync = (bool (*)(void *))tcadbsync;
  skel->optimize = (bool (*)(void *, const char *))tcadboptimize;
  skel->vanish = (bool (*)(void *))tcadbvanish;
  skel->copy = (bool (*)(void *, const char *))tcadbcopy; //TODO: Copy From maybe need RE-INDEX
  skel->tranbegin = (bool (*)(void *))tcadbtranbegin;
  skel->trancommit = (bool (*)(void *))tcadbtrancommit;
  skel->tranabort = (bool (*)(void *))tcadbtranabort;
  skel->path = (const char *(*)(void *))tcadbpath;
  skel->rnum = (uint64_t (*)(void *))tcadbrnum;
  skel->size = (uint64_t (*)(void *))tcadbsize;
  skel->misc = (TCLIST *(*)(void *, const char *, const TCLIST *))tcADBmisc;
  skel->putproc =
  (bool (*)(void *, const void *, int, const void *, int, TCPDPROC, void *))tcadbputproc;
  skel->foreach = (bool (*)(void *, TCITER, void *))tcadbforeach;
  
}
/* Set the b+tree database skeleton to an abstract database object. */
bool tcADBSetSkelBDB(TCADB *adb){
#if defined(DEBUG)
  //fprintf(stderr, "%s\n", "tcADBSetSkelBDB");
#endif
  assert(adb);
  if(adb->omode != ADBOVOID) return false;
  ADBSKEL skel;
  memset(&skel, 0, sizeof(skel));
  tcBDBSkelSet(&skel);
  if(!tcadbsetskel(adb, &skel)){
    tcadbdel(skel.opq);
    return false;
  }
  return true;
}

bool initialize(ADBSKEL *skel){
  tcBDBSkelSet(skel);
  return true;
}


void tcSkelBDBSetEcode(TCBDB *bdb, int aErrCode, const char *aMsg, const char *filename, int line, const char *func){
  assert(bdb && filename && line >= 1 && func);
  char *s = tcsprintf("%s:%s",func, aMsg);
  tchdbsetecode(bdb->hdb, aErrCode, filename, line, s);
  free(s);
}
//*/

//const char *tcSkelBDBErrMsg(int ecode){
//  switch (ecode) {
//    case TCEREMOVEINDEX: return "remove an index failed.";
//    default: return tcbdberrmsg(ecode);
//  }
//}

/*************************************************************************************************
 * private features
 *************************************************************************************************/
static bool tcADBOpen(TCADB *adb, const char *name){
  assert(adb && name);
  if(adb->omode != ADBOVOID) return false;
  TCLIST *elems = tcstrsplit(name, "#");
  char *path = tclistshift2(elems);
  if(!path){
    tclistdel(elems);
    return false;
  }
  int dbgfd = -1;
  int64_t bnum = -1;
  int64_t capnum = -1;
  int64_t capsiz = -1;
  bool owmode = true;
  bool ocmode = true;
  bool otmode = false;
  bool onlmode = false;
  bool onbmode = false;
  int8_t apow = -1;
  int8_t fpow = -1;
  bool tlmode = false;
  bool tdmode = false;
  bool tbmode = false;
  bool ttmode = false;
  int32_t rcnum = -1;
  int64_t xmsiz = -1;
  int32_t dfunit = -1;
  int32_t lmemb = -1;
  int32_t nmemb = -1;
  int32_t lcnum = -1;
  int32_t ncnum = -1;
  int32_t width = -1;
  int64_t limsiz = -1;
  int ln = TCLISTNUM(elems);
  for(int i = 0; i < ln; i++){
    const char *elem = TCLISTVALPTR(elems, i);
    char *pv = strchr(elem, '=');
    if(!pv) continue;
    *(pv++) = '\0';
    if(!tcstricmp(elem, "dbgfd")){
      dbgfd = tcatoi(pv);
    } else if(!tcstricmp(elem, "bnum")){
      bnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capnum")){
      capnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "capsiz")){
      capsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "mode")){
      owmode = strchr(pv, 'w') || strchr(pv, 'W');
      ocmode = strchr(pv, 'c') || strchr(pv, 'C');
      otmode = strchr(pv, 't') || strchr(pv, 'T');
      onlmode = strchr(pv, 'e') || strchr(pv, 'E');
      onbmode = strchr(pv, 'f') || strchr(pv, 'F');
    } else if(!tcstricmp(elem, "apow")){
      apow = tcatoix(pv);
    } else if(!tcstricmp(elem, "fpow")){
      fpow = tcatoix(pv);
    } else if(!tcstricmp(elem, "opts")){
      if(strchr(pv, 'l') || strchr(pv, 'L')) tlmode = true;
      if(strchr(pv, 'd') || strchr(pv, 'D')) tdmode = true;
      if(strchr(pv, 'b') || strchr(pv, 'B')) tbmode = true;
      if(strchr(pv, 't') || strchr(pv, 'T')) ttmode = true;
    } else if(!tcstricmp(elem, "rcnum")){
      rcnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "xmsiz")){
      xmsiz = tcatoix(pv);
    } else if(!tcstricmp(elem, "dfunit")){
      dfunit = tcatoix(pv);
    } else if(!tcstricmp(elem, "lmemb")){
      lmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "nmemb")){
      nmemb = tcatoix(pv);
    } else if(!tcstricmp(elem, "lcnum")){
      lcnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "ncnum")){
      ncnum = tcatoix(pv);
    } else if(!tcstricmp(elem, "width")){
      width = tcatoix(pv);
    } else if(!tcstricmp(elem, "limsiz")){
      limsiz = tcatoix(pv);
    } 
  }
  tclistdel(elems);
  adb->omode = ADBOVOID;
  if(tcstribwm(path, ".tcb") || tcstribwm(path, ".bdb")){
    TCBDB *bdb = tcbdbnew();
    if(dbgfd >= 0) tcbdbsetdbgfd(bdb, dbgfd);
    tcbdbsetmutex(bdb);
    int opts = 0;
    if(tlmode) opts |= BDBTLARGE;
    if(tdmode) opts |= BDBTDEFLATE;
    if(tbmode) opts |= BDBTBZIP;
    if(ttmode) opts |= BDBTTCBS;
    tcbdbtune(bdb, lmemb, nmemb, bnum, apow, fpow, opts);
    tcbdbsetcache(bdb, lcnum, ncnum);
    if(xmsiz >= 0) tcbdbsetxmsiz(bdb, xmsiz);
    if(dfunit >= 0) tcbdbsetdfunit(bdb, dfunit);
    if(capnum > 0) tcbdbsetcapnum(bdb, capnum);
    int omode = owmode ? BDBOWRITER : BDBOREADER;
    if(ocmode) omode |= BDBOCREAT;
    if(otmode) omode |= BDBOTRUNC;
    if(onlmode) omode |= BDBONOLCK;
    if(onbmode) omode |= BDBOLCKNB;
    if(!tcbdbopen(bdb, path, omode)){
      tcbdbdel(bdb);
      TCFREE(path);
      return false;
    }
    adb->bdb = bdb;
    adb->cur = tcbdbcurnew(bdb);
    adb->omode = ADBOBDB;
  }
  TCFREE(path);
  if(adb->omode == ADBOVOID) return false;
  return true;
}

/* JSON -----
  Return the Parsed string if success. and the aValue will be skiped the position of the string.
  u need free the string to dispose the resource.
 NOte: CHAR MUST use the '', string/array use the "" in C.
 */
/*
static const char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(const char **aValue){
  const char vSep = **aValue;
	const char *ptr=*aValue+1;char *ptr2;char *out;int len=0;unsigned uc;
  //aStrLen = 0;
  if (vSep != '\'' && vSep != '"') return 0; //not a string.
  
  while (*ptr!=vSep && (unsigned char)*ptr>31 && ++len) if (*ptr++ == '\\') ptr++;	// Skip escaped quotes.
  
  //fprintf(stderr,"l:%d\n",len);
  //aValue = skip(aValue+1);
  //fprintf(stderr,"sl:%d\n",len);
	out = (char*)malloc(len+1);	// This is how long we need for the string, roughly.
  //fprintf(stderr,"l:%d\n",len);
	if (!out) return 0;
  
	ptr=*aValue + 1;ptr2=out;
	while ( *ptr!=vSep && (unsigned char)*ptr>31)
	{
		if (*ptr!='\\') *ptr2++=*ptr++;
		else
		{
			ptr++;
			switch (*ptr)
			{
				case 'b': *ptr2++='\b';	break;
				case 'f': *ptr2++='\f';	break;
				case 'n': *ptr2++='\n';	break;
				case 'r': *ptr2++='\r';	break;
				case 't': *ptr2++='\t';	break;
				case 'u':	 // transcode utf16 to utf8. DOES NOT SUPPORT SURROGATE PAIRS CORRECTLY.
					sscanf(ptr+1,"%4x",&uc);	// get the unicode char.
					len=3;if (uc<0x80) len=1;else if (uc<0x800) len=2;ptr2+=len;
					
					switch (len) {
						case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
						case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6;
						case 1: *--ptr2 =(uc | firstByteMark[len]);
					}
					ptr2+=len;ptr+=4;
					break;
				default:  *ptr2++=*ptr; break;
			}
			ptr++;
		}
	}
	*ptr2=0;
	if (*ptr==vSep) ptr++;
  *aValue = ptr;
  return out;
}
//*/
static bool tcBDBIndexRemove(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz) {
  bool vResult = tcbdbtranbegin(aDB); //TODO: this(transaction) will be a problem in the future.
#if defined(DEBUG)
  //fprintf(stderr, "begin remove index %s .\n", aFieldbuf);
#endif

  if (vResult) {
    char vIdxName[MAXKEYLEN+1];
    strcpy(vIdxName, IDXKEY);
    strncat(vIdxName, aFieldbuf, aFieldSiz);
    strcat(vIdxName, "/");
    //vResult = tcbdbout3(aDB, vIdxName, strlen(vIdxName));
    TCLIST *vList = tcbdbfwmkeys2(aDB, vIdxName, MAXLISTSIZE);
    int vCount = 0;
    if (vList) vCount = tclistnum(vList);
    while (vCount > 0 && vResult) {
      //vCount--;
      for (int i= 0; i < vCount; i++) {
        const char *kbuf;
        int ksiz;
        TCLISTVAL(kbuf, vList, i, ksiz);
#if defined(DEBUG)
//        fprintf(stderr, "remove the index %s count:%d.\n", kbuf, vCount);
#endif
        vResult = tcbdbout3(aDB, kbuf, ksiz);
#if defined(DEBUG)
//        fprintf(stderr, "remove the index %s end.\n", kbuf);
#endif
        if (!vResult) {
#if defined(DEBUG)
          fprintf(stderr, "remove the index %s failed: %s.\n", kbuf, tcbdberrmsg(tcbdbecode(aDB)));
#endif
          tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
          break;
        }
      }
      if (vResult) {
        tclistdel(vList);
        vList = tcbdbfwmkeys2(aDB, vIdxName, MAXLISTSIZE);
        if (vList) vCount = tclistnum(vList); else vCount = 0;
      }
    } 
    if (vList) tclistdel(vList);
    if (vResult) {
      strcpy(vIdxName, IDXCONFIGKEY);
      strncat(vIdxName, aFieldbuf, aFieldSiz);
      vResult = tcbdbout2(aDB, vIdxName);
      if (vResult) {
        vResult = tcbdbtrancommit(aDB);
#if defined(DEBUG)
//        fprintf(stderr, "remove the index %s ok.\n", aFieldbuf);
#endif
      }
      else {
        tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
        tcbdbtranabort(aDB);
#if defined(DEBUG)
        fprintf(stderr, "remove Index Config %s failed.\n", vIdxName);
#endif
      }
    }
    else {
#if defined(DEBUG)
      fprintf(stderr, "remove Index %s failed: %s.\n", vIdxName, tcbdberrmsg(tcbdbecode(aDB)));
#endif
      tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
      tcbdbtranabort(aDB);
    }
    
  }
  else {
#if defined(DEBUG)
    fprintf(stderr, "remove Index %s can not begin transaction failed.\n", aFieldbuf);
#endif
    tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
  }

  return vResult;
}

static bool tcBDBReIndex(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt){
  bool vResult = true;tcbdbtranbegin(aDB); //TODO: this(transaction) will be a performance problem in the future.
#if defined(DEBUG)
//  fprintf(stderr, "begin re-index %s .\n", aFieldbuf);
#endif
  if (vResult) {
    char vIdxName[MAXKEYLEN+1];
//    strcpy(vIdxName, IDXKEY);
//    strncat(vIdxName, aFieldbuf, aFieldSiz);
//    strcat(vIdxName, "/");
    TCLIST *vList = tcbdbrange(aDB, NULL, 0, true, NULL, 0, true, MAXLISTSIZE);
    int vCount = 0;
    if (vList) vCount = tclistnum(vList);
    while (vCount > 0 && vResult) {
      json_t *vRoot;
      json_error_t vError;

      const char *kbuf, *vbuf;
      int ksiz, vsiz;
      for (int i= 0; i < vCount; i++) {//through out the fields:
        TCLISTVAL(kbuf, vList, i, ksiz);
#if defined(DEBUG)
//        fprintf(stderr, "reindex the index begin %s i:%d.\n", kbuf, i);
#endif
        if (isKeepReservedChar(kbuf)) {
          continue;
        }
#if defined(DEBUG)
//        fprintf(stderr, "reindex the index %s i:%d.\n", kbuf, i);
#endif
        vbuf = tcbdbget2(aDB, kbuf);
        vsiz = strlen(vbuf);

        //Now Parse the value and index it.        
        vRoot = json_loads(vbuf, &vError); //if (json_is_object(vRoot))
        if (vRoot) {
          const char *vFieldKey;
          json_t *vFieldValue;
          //char vIdxName[MAXKEYLEN+1];
          int vIsIndex;
          void *iter = json_object_iter(vRoot);
          while(iter)
          {
            vFieldKey = json_object_iter_key(iter);
            if (strcmp(vFieldKey, aFieldbuf) == 0) {
              vFieldValue = json_object_iter_value(iter);
#if defined(DEBUG)
//              fprintf(stderr, "Find index field(%s) \n", vFieldKey);
#endif
              
              //vIdxName = tcsprintf("%s%s", IDXCONFIGKEY, key);
              if ((strlen(IDXCONFIGKEY)+strlen(vFieldKey) > MAXKEYLEN)) {
                vResult = false;
                tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
                tcbdbtranabort(aDB);
#if defined(DEBUG)
                fprintf(stderr, "Put field too long(%s) \n", vFieldKey);
#endif
                break;
              }
              if (json_is_object(vFieldValue) || json_is_array(vFieldValue)) {
                continue;
              }
              //            strcpy(vIdxName, IDXCONFIGKEY);
              //            strcat(vIdxName, vFieldKey);
              //            vIsIndex = tcbdbvsiz2(aDB, vIdxName);
              //TCFREE(vIdxName);
              //            if (vIsIndex >= 0) { //it is the index field, need to store the index.
              strcpy(vIdxName, IDXKEY);
              strcat(vIdxName, vFieldKey);
              strcat(vIdxName, "/");
              StrCatJson(vIdxName, vFieldValue);
              
              strcat(vIdxName, "/");
              strncat(vIdxName, kbuf, ksiz);
              vResult = tcbdbput(aDB, vIdxName, strlen(vIdxName), kbuf, ksiz);//*[bug] The duplication index(FieldValue) not supported. TC can not support the putdup in tranbegin...trancommit
              if (!vResult) {
                tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
                tcbdbtranabort(aDB);
#if defined(DEBUG)
                fprintf(stderr, "can not save the index(%s)\n", vIdxName);
#endif
                break;
              }
              //            } //if (vIsIndex >= 0)
            }
            
            iter = json_object_iter_next(vRoot, iter);
          }
          json_decref(vRoot);    
        }
        
#if defined(DEBUG)
        //        fprintf(stderr, "reindex the index %s end.\n", kbuf);
#endif
      } //for structure-value through out field
      if (vResult) {
        TCLIST *t = vList;
        vList = tcbdbrange(aDB, kbuf, ksiz, false, NULL, 0, true, MAXLISTSIZE);
        tclistdel(t);
        if (vList) {
          vCount = tclistnum(vList);
#if defined(DEBUG)
//          fprintf(stderr, "Find the count %d.\n", vCount);
#endif
        }
        else vCount = 0;
      }
      else {
        break;
      }

    } //while 
    if (vList) tclistdel(vList);
    if (vResult) {
        vResult = tcbdbtrancommit(aDB);
    }
    else {
      tcbdbtranabort(aDB);
#if defined(DEBUG)
      fprintf(stderr, "reIndex %s failed: %s.\n", aFieldbuf, tcbdberrmsg(tcbdbecode(aDB)));
#endif
      tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    }
    
  }
  else {
#if defined(DEBUG)
    fprintf(stderr, "reIndex %s can not begin transaction failed.\n", aFieldbuf);
#endif
    tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
  }
  
  return vResult;  
}

static bool tcBDBIndexSet(TCBDB *aDB, const char *aFieldbuf, int aFieldSiz, const char *aOptions, int aOptionsSiz, int aOptionInt){
  bool vResult;
  char vIdxName[MAXKEYLEN+1];
  strcpy(vIdxName, IDXCONFIGKEY);
  strcat(vIdxName, aFieldbuf);
//  if (aOptionInt == -1) {
//    aOptionInt = tctdbstrtoindextype(aOptions);
//  }
//  vResult = tcBDBReIndex(aDB, aFieldbuf, aFieldSiz, aOptions, aOptionsSiz, aOptionInt);
//  if (vResult) {
    vResult = tcbdbput(aDB, vIdxName, strlen(vIdxName), aOptions, aOptionsSiz);
//  }
  return vResult;
}

static bool tcBDBDelete(TCBDB *aDB, const char *kbuf, int ksiz, const TCBDBOUTPROC aOutProc){
  bool vResult = false;
  json_t *vRoot;
  json_error_t vError;
  
  if (tcbdbtranbegin(aDB)) {
    int vsiz;
    char *vbuf = tcbdbget(aDB, kbuf, ksiz, &vsiz);
    if (vbuf) { //* [bug] delete a record not exists will be fatal error: Segmentation fault
      vRoot = json_loads(vbuf, &vError); //if (json_is_object(vRoot))
      if (vRoot) {
        //char numbuf[TCNUMBUFSIZ];
        const char *vFieldKey;
        json_t *vFieldValue;
        char vIdxName[MAXKEYLEN+1];
        int vIsIndex;
        void *iter = json_object_iter(vRoot);
        while(iter)
        {
          vFieldKey = json_object_iter_key(iter);
          vFieldValue = json_object_iter_value(iter);
          
          //vIdxName = tcsprintf("%s%s", IDXCONFIGKEY, key);
          if ((strlen(IDXCONFIGKEY)+strlen(vFieldKey) > MAXKEYLEN)) {
            vResult = false;
            tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
            tcbdbtranabort(aDB);
#if defined(DEBUG)
            fprintf(stderr, "the field too long(%s) \n", vFieldKey);
#endif
            break;
          }
          if (json_is_object(vFieldValue) || json_is_array(vFieldValue)) {
            continue;
          }
          strcpy(vIdxName, IDXCONFIGKEY);
          strcat(vIdxName, vFieldKey);
          vIsIndex = tcbdbvsiz2(aDB, vIdxName);
          //TCFREE(vIdxName);
          if (vIsIndex >= 0) { //it is the index field, need to store the index.
            strcpy(vIdxName, IDXKEY);
            strcat(vIdxName, vFieldKey);
            strcat(vIdxName, "/");
            StrCatJson(vIdxName, vFieldValue);
            
            strcat(vIdxName, "/");
            strncat(vIdxName, kbuf, ksiz);
            vResult = tcbdbout(aDB, vIdxName, strlen(vIdxName));
          }
          
          iter = json_object_iter_next(vRoot, iter);
        } //end-while
        json_decref(vRoot);    
      }
      else {
        vResult = true;
      }
    } //end-if(vbuf)
    vResult = aOutProc(aDB, kbuf, ksiz);  //* [Bug] can not delete a record if no index exists.
    if (vResult) {
      vResult = tcbdbtrancommit(aDB);
    } 
    else {
      tcbdbtranabort(aDB);
    }
  } //tcadbtran
  else {
    vResult = aOutProc(aDB, kbuf, ksiz);
  }  
  return vResult;
  
}

static bool tcADBDelete(TCADB *aDB, const char *kbuf, int ksiz){
  bool vResult = false;
  json_t *vRoot;
  json_error_t vError;
  
  if (tcadbtranbegin(aDB)) {
    int vsiz;
    char *vbuf = tcadbget(aDB, kbuf, ksiz, &vsiz);
    if (vbuf) {
      vRoot = json_loads(vbuf, &vError); //if (json_is_object(vRoot))
      if (vRoot) {
        //char numbuf[TCNUMBUFSIZ];
        const char *vFieldKey;
        json_t *vFieldValue;
        char vIdxName[MAXKEYLEN+1];
        int vIsIndex;
        void *iter = json_object_iter(vRoot);
        while(iter)
        {
          vFieldKey = json_object_iter_key(iter);
          vFieldValue = json_object_iter_value(iter);
          
          //vIdxName = tcsprintf("%s%s", IDXCONFIGKEY, key);
          if ((strlen(IDXCONFIGKEY)+strlen(vFieldKey) > MAXKEYLEN)) {
            vResult = false;
            tcbdbsetecode(aDB->bdb, TCEINVALID, __FILE__, __LINE__, __func__);
            tcadbtranabort(aDB);
            break;
          }
          if (json_is_object(vFieldValue) || json_is_array(vFieldValue)) {
            continue;
          }
          strcpy(vIdxName, IDXCONFIGKEY);
          strcat(vIdxName, vFieldKey);
          vIsIndex = tcadbvsiz2(aDB, vIdxName);
          //TCFREE(vIdxName);
          if (vIsIndex >= 0) { //it is the index field, need to store the index.
            strcpy(vIdxName, IDXKEY);
            strcat(vIdxName, vFieldKey);
            strcat(vIdxName, "/");
            StrCatJson(vIdxName, vFieldValue);
            
            strcat(vIdxName, "/");
            strncat(vIdxName, kbuf, ksiz);
            vResult = tcadbout(aDB, vIdxName, strlen(vIdxName));
          }
          
          iter = json_object_iter_next(vRoot, iter);
        } //end-while(iter)
        json_decref(vRoot);    
      }
      else {
        vResult = true;
      }
    } 

    vResult = tcadbout(aDB, kbuf, ksiz);
    if (vResult) vResult = tcadbtrancommit(aDB); else tcadbtranabort(aDB);
  }
  else {
    vResult = tcadbout(aDB, kbuf, ksiz);
  }  
  return vResult;
  
}

static bool tcBDBReplace(TCBDB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCBDBPUTPROC aPutProc){
  bool vResult = false;
  if (isKeepReservedChar(kbuf)) {
#if defined(DEBUG)
    fprintf(stderr, "Put error: the key is keep reserved(%s)\n", kbuf);
#endif
    tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
    return vResult;
  }
  //char *vJsonStr = skip(vbuf+1);
  
  json_t *vRoot;
  json_error_t vError;
  
  vRoot = json_loads(vbuf, &vError); //if (json_is_object(vRoot))
  if (vRoot) {
    if (tcbdbtranbegin(aDB)) {
      //char numbuf[TCNUMBUFSIZ];
      const char *vFieldKey;
      json_t *vFieldValue;
      char vIdxName[MAXKEYLEN+1];
      int vIsIndex;
      void *iter = json_object_iter(vRoot);
      while(iter)
      {
        vFieldKey = json_object_iter_key(iter);
        vFieldValue = json_object_iter_value(iter);
        
        //vIdxName = tcsprintf("%s%s", IDXCONFIGKEY, key);
        if ((strlen(IDXCONFIGKEY)+strlen(vFieldKey) > MAXKEYLEN)) {
          vResult = false;
          tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
          tcbdbtranabort(aDB);
#if defined(DEBUG)
          fprintf(stderr, "Put field too long(%s) \n", vFieldKey);
#endif
          break;
        }
        if (json_is_object(vFieldValue) || json_is_array(vFieldValue)) {
          continue;
        }
        strcpy(vIdxName, IDXCONFIGKEY);
        strcat(vIdxName, vFieldKey);
        vIsIndex = tcbdbvsiz2(aDB, vIdxName);
        //TCFREE(vIdxName);
        if (vIsIndex >= 0) { //it is the index field, need to store the index.
          strcpy(vIdxName, IDXKEY);
          strcat(vIdxName, vFieldKey);
          strcat(vIdxName, "/");
          StrCatJson(vIdxName, vFieldValue);
          
          strcat(vIdxName, "/");
          strncat(vIdxName, kbuf, ksiz);
          vResult = tcbdbput(aDB, vIdxName, strlen(vIdxName), kbuf, ksiz);//*[bug] The duplication index(FieldValue) not supported. TC can not support the putdup in tranbegin...trancommit
          if (!vResult) {
            tcbdbsetecode(aDB, TCEINVALID, __FILE__, __LINE__, __func__);
            tcbdbtranabort(aDB);
#if defined(DEBUG)
            fprintf(stderr, "can not save the index(%s)\n", vIdxName);
#endif
            break;
          }
        }
        else {
          vResult = true;
#if defined(DEBUG)
          fprintf(stderr, "this field %s is not indexed.\n", vFieldKey);
#endif
        }
        
        iter = json_object_iter_next(vRoot, iter);
      }
      if (vResult) vResult = aPutProc(aDB, kbuf, ksiz, vbuf, vsiz);
      if (vResult) {
        vResult = tcbdbtrancommit(aDB); 
#if defined(DEBUG)
        if (!vResult) fprintf(stderr, "can not trancommit the key/value(%s:%s)\n", kbuf, vbuf);
#endif      
      } 
      else {
        tcbdbtranabort(aDB);      
#if defined(DEBUG)
        fprintf(stderr, "can not put the key/value(%s:%s)\n", kbuf, vbuf);
#endif
      }
    } //tcadbtran
    json_decref(vRoot);    
  }
  else {
    vResult = aPutProc(aDB, kbuf, ksiz, vbuf, vsiz);
  }  
  return vResult;
  
}

static bool tcADBReplace(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz, const TCADBPUTPROC aPutProc){
  bool vResult = false;
  if (isKeepReservedChar(kbuf)) {
#if defined(DEBUG)
    fprintf(stderr, "Put error: the key is keep reserved(%s)\n", kbuf);
#endif
    
    errno = EACCES;
    tcbdbsetecode(aDB->bdb, TCEINVALID, __FILE__, __LINE__, __func__);
    return vResult;
  }
  
  //char *vJsonStr = skip(vbuf+1);
  
  json_t *vRoot;
  json_error_t vError;
  
  vRoot = json_loads(vbuf, &vError); //if (json_is_object(vRoot))
  if (vRoot) {
    if (tcadbtranbegin(aDB)) {
      //char numbuf[TCNUMBUFSIZ];
      const char *vFieldKey;
      json_t *vFieldValue;
      char vIdxName[MAXKEYLEN+1];
      int vIsIndex;
      void *iter = json_object_iter(vRoot);
      while(iter)
      {
        vFieldKey = json_object_iter_key(iter);
        vFieldValue = json_object_iter_value(iter);
        
        //vIdxName = tcsprintf("%s%s", IDXCONFIGKEY, key);
        if ((strlen(IDXCONFIGKEY)+strlen(vFieldKey) > MAXKEYLEN)) {
          vResult = false;
          tcbdbsetecode(aDB->bdb, TCEINVALID, __FILE__, __LINE__, __func__);
          tcadbtranabort(aDB);
#if defined(DEBUG)
          fprintf(stderr, "Put field too long(%s) \n", vFieldKey);
#endif
          break;
        }
        if (json_is_object(vFieldValue) || json_is_array(vFieldValue)) {
          continue;
        }
        strcpy(vIdxName, IDXCONFIGKEY);
        strcat(vIdxName, vFieldKey);
        vIsIndex = tcadbvsiz2(aDB, vIdxName);
        //TCFREE(vIdxName);
        if (vIsIndex >= 0) { //it is the index field, need to store the index.
#if defined(DEBUG)
          //fprintf(stderr, "begin to store index(%s)\n", vFieldKey);
#endif
          strcpy(vIdxName, IDXKEY);
          strcat(vIdxName, vFieldKey);
          strcat(vIdxName, "/");
          StrCatJson(vIdxName, vFieldValue);
          
          strcat(vIdxName, "/");
          strncat(vIdxName, kbuf, ksiz);
          vResult = tcadbput(aDB, vIdxName, strlen(vIdxName), kbuf, ksiz);
          if (!vResult) {
            tcbdbsetecode(aDB->bdb, TCEINVALID, __FILE__, __LINE__, __func__);
            tcadbtranabort(aDB);
#if defined(DEBUG)
            fprintf(stderr, "can not save the index(%s)\n", vIdxName);
#endif
            break;
          }
        }
        else {
          vResult = true;
#if defined(DEBUG)
          //fprintf(stderr, "this field %s is not indexed.\n", vFieldKey);
#endif
        }

        
        iter = json_object_iter_next(vRoot, iter);
      }
      if (vResult) vResult = aPutProc(aDB, kbuf, ksiz, vbuf, vsiz);
      if (vResult) {        
        vResult = tcadbtrancommit(aDB);
#if defined(DEBUG)
        fprintf(stderr, "put %s tran :%d\n", kbuf, vResult);
#endif
        
      }
      else 
        tcadbtranabort(aDB);
    } //tcadbtran
#if defined(DEBUG)
    else {
      fprintf(stderr, "put tran failed:%s\n", kbuf);
    }
#endif

    json_decref(vRoot);    
  }
  else {
    vResult = aPutProc(aDB, kbuf, ksiz, vbuf, vsiz);
  }  
  return vResult;
}

/* Store a record into a database object.
   `aDB' specifies the database object.
   `kbuf' specifies the pointer to the region of the key.
   `ksiz' specifies the size of the region of the key.
   `vbuf' specifies the pointer to the region of the value.
   `vsiz' specifies the size of the region of the value.
   If successful, the return value is true, else, it is false. */
static bool tcADBput(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  bool vResult;

  if (vsiz > 4 && vbuf[0] == '{' && vbuf[vsiz-1] == '}') {
    vResult = tcADBReplace(aDB, (void *)kbuf, ksiz, (void *)vbuf, vsiz, tcadbput);
  }
  else {
    vResult = tcadbput(aDB, kbuf, ksiz, vbuf, vsiz);
  }
  return vResult;
}


/* Store a new record into a database object.
   `aDB' specifies the database object.
   `kbuf' specifies the pointer to the region of the key.
   `ksiz' specifies the size of the region of the key.
   `vbuf' specifies the pointer to the region of the value.
   `vsiz' specifies the size of the region of the value.
   If successful, the return value is true, else, it is false. */
static bool tcADBputkeep(TCADB *aDB, const char *kbuf, int ksiz, const char *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  bool vResult;
  
  if (vsiz > 4 && *vbuf == '{' && *(vbuf+vsiz-1) == '}') {
    vResult = tcADBReplace(aDB, kbuf, ksiz, vbuf, vsiz, tcadbputkeep);
  }
  else {
    vResult = tcadbputkeep(aDB, kbuf, ksiz, vbuf, vsiz);
  }
  return vResult;
}


/* Concatenate a value at the end of the existing record in a database object.
   `aDB' specifies the multiple database object.
   `kbuf' specifies the pointer to the region of the key.
   `ksiz' specifies the size of the region of the key.
   `vbuf' specifies the pointer to the region of the value.
   `vsiz' specifies the size of the region of the value.
   If successful, the return value is true, else, it is false. */
/*
static bool tcMeDBputcat(TCADB *aDB, const void *kbuf, int ksiz, const void *vbuf, int vsiz){
  assert(aDB && kbuf && ksiz >= 0 && vbuf && vsiz >= 0);
  bool vResult;
  
  if (vsiz > 4 && *vbuf[0] == "{" && *vbuf[vsiz-1] == "}") {
    vResult = tcMeIndexReplace(aDB, kbuf, ksiz, vbuf, vsiz, tcadbputkeep);
  }
  else {
    vResult = tcadbputcat(aDB, kbuf, ksiz, vbuf, vsiz);
  }
  return vResult;
}
*/

/* Remove a record of a multiple database object.
   `aDB' specifies the multiple database object.
   `kbuf' specifies the pointer to the region of the key.
   `ksiz' specifies the size of the region of the key.
   If successful, the return value is true, else, it is false. */
static bool tcADBOut(TCADB *aDB, const char *kbuf, int ksiz){
  assert(aDB && kbuf && ksiz >= 0);
  
  return tcADBDelete(aDB, kbuf, ksiz);
}

/* Lock a method of the B+ tree database object.
 `bdb' specifies the B+ tree database object.
 `wr' specifies whether the lock is writer or not.
 If successful, the return value is true, else, it is false. */
static bool tcbdblockmethod(TCBDB *bdb, bool wr){
  assert(bdb);
  if(wr ? pthread_rwlock_wrlock(bdb->mmtx) != 0 : pthread_rwlock_rdlock(bdb->mmtx) != 0){
    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}


/* Unlock a method of the B+ tree database object.
 `bdb' specifies the B+ tree database object.
 If successful, the return value is true, else, it is false. */
static bool tcbdbunlockmethod(TCBDB *bdb){
  assert(bdb);
  if(pthread_rwlock_unlock(bdb->mmtx) != 0){
    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}


/* Lock the cache of the B+ tree database object.
 `bdb' specifies the B+ tree database object.
 If successful, the return value is true, else, it is false. */
static bool tcbdblockcache(TCBDB *bdb){
  assert(bdb);
  if(pthread_mutex_lock(bdb->cmtx) != 0){
    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}


/* Unlock the cache of the B+ tree database object.
 `bdb' specifies the B+ tree database object.
 If successful, the return value is true, else, it is false. */
static bool tcbdbunlockcache(TCBDB *bdb){
  assert(bdb);
  if(pthread_mutex_unlock(bdb->cmtx) != 0){
    tcbdbsetecode(bdb, TCETHREAD, __FILE__, __LINE__, __func__);
    return false;
  }
  TCTESTYIELD();
  return true;
}

/* Generate a unique ID number.
 `tdb' specifies the table database object.
 `inc' specifies the increment of the seed.
 The return value is the new unique ID number or -1 on failure. */
/*
static int64_t tcbdbgenuidimpl(TCBDB *tdb, int64_t inc){
  assert(tdb);
  void *opq = tchdbopaque(tdb->hdb);
  uint64_t llnum, uid;
  if(inc < 0){
    uid = -inc - 1;
  } else {
    memcpy(&llnum, opq, sizeof(llnum));
    if(inc == 0) return TCITOHLL(llnum);
    uid = TCITOHLL(llnum) + inc;
  }
  llnum = TCITOHLL(uid);
  memcpy(opq, &llnum, sizeof(llnum));
  return uid;
}
*/
/* Generate a unique ID number of a b+tree database object. */
/*
int64_t tcbdbgenuid(TCBDB *tdb){
  assert(tdb);
  if(!BDBLOCKMETHOD(tdb, true)) return -1;
  if(!tdb->open || !tdb->wmode){
    tcbdbsetecode(tdb, TCEINVALID, __FILE__, __LINE__, __func__);
    BDBUNLOCKMETHOD(tdb);
    return -1;
  }
  int64_t rv = tcbdbgenuidimpl(tdb, 1);
  BDBUNLOCKMETHOD(tdb);
  return rv;
}
*/

/* Call a versatile function for miscellaneous operations of a database object.
   `aDB' specifies the multiple database object.
   `name' specifies the name of the function.
   `args' specifies a list object containing arguments.
   If successful, the return value is a list object of the result.
 */
static TCLIST *tcADBmisc(TCADB *aDB, const char *name, const TCLIST *args){
  assert(aDB && name && args);
  int argc = TCLISTNUM(args);
#if defined(DEBUG)
  fprintf(stderr, "DBMisc(%s) argc=%d\n", name, argc);
  for(int i = 0; i < argc; i++){
    const char *vbuf;
    int vsiz;
    TCLISTVAL(vbuf, args, i, vsiz);
    fprintf(stderr, "  args[%d]: %s\n", i, vbuf);
  }
#endif
  TCLIST *rv;
  ADBSKEL *skel;
  switch(aDB->omode){
    case ADBOBDB:
      if(!strcmp(name, "put") || !strcmp(name, "putkeep") || !strcmp(name, "putcat") ||
         !strcmp(name, "putdup") || !strcmp(name, "putdupback")){
        if(argc > 1){
          rv = tclistnew2(1);
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, 0, ksiz);
          const char *vbuf;
          int vsiz;
          TCLISTVAL(vbuf, args, 1, vsiz);
          bool err = false;
          if (vsiz > 4 && *vbuf == '{' && *(vbuf+vsiz-1) == '}') {
            err = tcADBReplace(aDB, kbuf, ksiz, vbuf, vsiz, tcadbput);
            if(!strcmp(name, "put")){
              err = !tcBDBReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbput);
            } else if(!strcmp(name, "putkeep")){
              err = !tcBDBReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbputkeep);
            } else if(!strcmp(name, "putcat")){
              if(!tcbdbputcat(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putdup")){
              if(!tcbdbputdup(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putdupback")){
              if(!tcbdbputdupback(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            }
            /*else if(!strcmp(name, "putcat")){
              err = !tcBDBIndexReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbputcat);
            } else if(!strcmp(name, "putdup")){
              err = !tcBDBIndexReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbputdup);
            } else if(!strcmp(name, "putdupback")){
              err = !tcBDBIndexReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbputdupback);
            }*/
          }
          else {
            if(!strcmp(name, "put")){
              if(!tcbdbput(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putkeep")){
              if(!tcbdbputkeep(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putcat")){
              if(!tcbdbputcat(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putdup")){
              if(!tcbdbputdup(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            } else if(!strcmp(name, "putdupback")){
              if(!tcbdbputdupback(aDB->bdb, kbuf, ksiz, vbuf, vsiz)) err = true;
            }
          }
          
          if(err){
            tclistdel(rv);
            rv = NULL;
#if defined(DEBUG)
            fprintf(stderr, "BDBReplace Error %s.\n", kbuf); //err = true;
#endif
          }
        } else {
          rv = NULL;
        }
      } else if(!strcmp(name, "out")){
        if(argc > 0){
          rv = tclistnew2(1);
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, 0, ksiz);
          if(!tcBDBDelete(aDB->bdb, kbuf, ksiz, tcbdbout)){
            tclistdel(rv);
            rv = NULL;
          }
        } else {
          rv = NULL;
        }
      } else if(!strcmp(name, "get")){
        if(argc > 0){
          rv = tclistnew2(1);
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, 0, ksiz);
          TCLIST *vals = tcbdbget4(aDB->bdb, kbuf, ksiz);
          if(vals){
            tclistdel(rv);
            rv = vals;
#if defined(DEBUG)
            fprintf(stderr, "DBMisc(%s) argc=%d\n", name, argc);
            for(int i = 0; i < TCLISTNUM(rv); i++){
              const char *vbuf;
              int vsiz;
              TCLISTVAL(vbuf, rv, i, vsiz);
              fprintf(stderr, "  value[%d]: %s\n", i, vbuf);
            }
#endif
          } else {
            tclistdel(rv);
            rv = NULL;
          }
        } else {
          rv = NULL;
        }
      } else if(!strcmp(name, "putlist")){
        rv = tclistnew2(1);
        bool err = false;
        argc--;
        for(int i = 0; i < argc; i += 2){
          const char *kbuf, *vbuf;
          int ksiz, vsiz;
          TCLISTVAL(kbuf, args, i, ksiz);
          TCLISTVAL(vbuf, args, i + 1, vsiz);
          if(!tcBDBReplace(aDB->bdb, kbuf, ksiz, vbuf, vsiz, tcbdbputdup)){
            err = true;
            break;
          }
        }
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "outlist")){
        rv = tclistnew2(1);
        bool err = false;
        for(int i = 0; i < argc; i++){
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, i, ksiz);
          if(!tcBDBDelete(aDB->bdb, kbuf, ksiz, tcbdbout3) && tcbdbecode(aDB->bdb) != TCENOREC){ //todo: tcbdbout3 can delete duplication record.
            err = true;
            break;
          }
        }
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "getlist")){
        rv = tclistnew2(argc * 2);
        bool err = false;
        for(int i = 0; i < argc; i++){
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, i, ksiz);
          TCLIST *vals = tcbdbget4(aDB->bdb, kbuf, ksiz);
          if(vals){
            int vnum = TCLISTNUM(vals);
            for(int j = 0; j < vnum; j++){
              TCLISTPUSH(rv, kbuf, ksiz);
              const char *vbuf;
              int vsiz;
              TCLISTVAL(vbuf, vals, j, vsiz);
              TCLISTPUSH(rv, vbuf, vsiz);
            }
            tclistdel(vals);
          } else if(tcbdbecode(aDB->bdb) != TCENOREC){
            err = true;
          }
        }
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "getpart")){
        if(argc > 0){
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, 0, ksiz);
          int off = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
          if(off < 0) off = 0;
          if(off > INT_MAX / 2 - 1) off = INT_MAX - 1;
          int len = argc > 2 ? tcatoi(TCLISTVALPTR(args, 2)) : -1;
          if(len < 0 || len > INT_MAX / 2) len = INT_MAX / 2;
          int vsiz;
          char *vbuf = tcbdbget(aDB->bdb, kbuf, ksiz, &vsiz);
          if(vbuf){
            if(off < vsiz){
              rv = tclistnew2(1);
              vsiz -= off;
              if(vsiz > len) vsiz = len;
              if(off > 0) memmove(vbuf, vbuf + off, vsiz);
              tclistpushmalloc(rv, vbuf, vsiz);
            } else {
              rv = NULL;
              TCFREE(vbuf);
            }
          } else {
            rv = NULL;
          }
        } else {
          rv = NULL;
        }
      } else if(!strcmp(name, "iterinit")){
        rv = tclistnew2(1);
        bool err = false;
        if(argc > 0){
          const char *kbuf;
          int ksiz;
          TCLISTVAL(kbuf, args, 0, ksiz);
          if(!tcbdbcurjump(aDB->cur, kbuf, ksiz)) err = true;
        } else {
          if(!tcbdbcurfirst(aDB->cur)) err = true;
        }
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "iternext")){
        rv = tclistnew2(1);
        int ksiz;
        const char *kbuf = tcbdbcurkey3(aDB->cur, &ksiz);
        if(kbuf){
          TCLISTPUSH(rv, kbuf, ksiz);
          int vsiz;
          const char *vbuf = tcbdbcurval3(aDB->cur, &vsiz);
          if(vbuf) TCLISTPUSH(rv, vbuf, vsiz);
          tcbdbcurnext(aDB->cur);
        } else {
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "setindex")){
#if defined(DEBUG)
//        fprintf(stderr, "%s\n", "setindex");
#endif
        rv = tclistnew2(1);
        bool err = false;
        argc--;
        for(int i = 0; i < argc; i += 2){
          const char *kbuf, *vbuf;
          int ksiz, vsiz;
          TCLISTVAL(kbuf, args, i, ksiz);
          TCLISTVAL(vbuf, args, i + 1, vsiz);
          int type = tctdbstrtoindextype(vbuf);
#if defined(DEBUG)
//          fprintf(stderr, "index.key=%s\n", kbuf);
//          fprintf(stderr, "index.opt='%s':%d\n", vbuf, type);
#endif
          if (type == TDBITVOID) {
            err = !tcBDBIndexRemove(aDB->bdb, kbuf, ksiz);
#if defined(DEBUG)
            if (err) {
              fprintf(stderr, "FieldName:%s; FieldOpt:%s Remove Index error! \n", kbuf, vbuf);
            }
#endif
          }else if (type == TDBITREINDEX) {
            err = !tcBDBReIndex(aDB->bdb, kbuf, ksiz, vbuf, vsiz, type);
          } else if(type >= 0){
            err = !tcBDBIndexSet(aDB->bdb, kbuf, ksiz, vbuf, vsiz, type);
#if defined(DEBUG)
            if (err) {
              fprintf(stderr, "FieldName:%s; FieldOpt:%s Set Index error! \n", kbuf, vbuf);
            }
#endif
          } else {
            err = true;
          }
        }
        if(err){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "sync")){
        rv = tclistnew2(1);
        if(!tcadbsync(aDB)){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "optimize")){
        rv = tclistnew2(1);
        const char *params = argc > 0 ? TCLISTVALPTR(args, 0) : NULL;
        if(!tcadboptimize(aDB, params)){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "vanish")){
        rv = tclistnew2(1);
        if(!tcadbvanish(aDB)){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "error")){
        rv = tclistnew2(1);
        int ecode = tcbdbecode(aDB->bdb);
        tclistprintf(rv, "%d: %s", ecode, tcbdberrmsg(ecode));
        uint8_t flags = tcbdbflags(aDB->bdb);
        if(flags & BDBFFATAL) tclistprintf(rv, "fatal");
      } else if(!strcmp(name, "defrag")){
        rv = tclistnew2(1);
        int64_t step = argc > 0 ? tcatoi(TCLISTVALPTR(args, 0)) : -1;
        if(!tcbdbdefrag(aDB->bdb, step)){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "cacheclear")){
        rv = tclistnew2(1);
        if(!tcbdbcacheclear(aDB->bdb)){
          tclistdel(rv);
          rv = NULL;
        }
      } else if(!strcmp(name, "regex")){
        if(argc > 0){
          const char *regex = TCLISTVALPTR(args, 0);
          int options = REG_EXTENDED | REG_NOSUB;
          if(*regex == '*'){
            options |= REG_ICASE;
            regex++;
          }
          regex_t rbuf;
          if(regcomp(&rbuf, regex, options) == 0){
            rv = tclistnew();
            int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
            if(max < 1) max = INT_MAX;
            BDBCUR *cur = tcbdbcurnew(aDB->bdb);
            tcbdbcurfirst(cur);
            TCXSTR *kxstr = tcxstrnew();
            TCXSTR *vxstr = tcxstrnew();
            while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
              const char *kbuf = TCXSTRPTR(kxstr);
              if(regexec(&rbuf, kbuf, 0, NULL, 0) == 0){
                TCLISTPUSH(rv, kbuf, TCXSTRSIZE(kxstr));
                TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
                max--;
              }
              tcbdbcurnext(cur);
            }
            tcxstrdel(vxstr);
            tcxstrdel(kxstr);
            tcbdbcurdel(cur);
            regfree(&rbuf);
          } else {
            rv = NULL;
          }
        } else {
          rv = NULL;
        }
      } else if(!strcmp(name, "range")){
        rv = tclistnew();
        int bksiz = 0;
        const char *bkbuf = NULL;
        if(argc > 0) TCLISTVAL(bkbuf, args, 0, bksiz);
        int max = argc > 1 ? tcatoi(TCLISTVALPTR(args, 1)) : 0;
        if(max < 1) max = INT_MAX;
        int eksiz = 0;
        const char *ekbuf = NULL;
        if(argc > 2) TCLISTVAL(ekbuf, args, 2, eksiz);
        TCCMP cmp = tcbdbcmpfunc(aDB->bdb);
        void *cmpop = tcbdbcmpop(aDB->bdb);
        BDBCUR *cur = tcbdbcurnew(aDB->bdb);
        if(bkbuf){
          tcbdbcurjump(cur, bkbuf, bksiz);
        } else {
          tcbdbcurfirst(cur);
        }
        TCXSTR *kxstr = tcxstrnew();
        TCXSTR *vxstr = tcxstrnew();
        while(max > 0 && tcbdbcurrec(cur, kxstr, vxstr)){
          const char *kbuf = TCXSTRPTR(kxstr);
          int ksiz = TCXSTRSIZE(kxstr);
          if(ekbuf && cmp(kbuf, ksiz, ekbuf, eksiz, cmpop) >= 0) break;
          TCLISTPUSH(rv, kbuf, ksiz);
          TCLISTPUSH(rv, TCXSTRPTR(vxstr), TCXSTRSIZE(vxstr));
          max--;
          tcbdbcurnext(cur);
        }
        tcxstrdel(vxstr);
        tcxstrdel(kxstr);
        tcbdbcurdel(cur);
      } else if(!strcmp(name, "genuid")){
        rv = tclistnew2(1);
        //int nsiz = sprintf(numbuf, "%lld", (long long)tcbdbgenuid(aDB->bdb));
        tclistpush2(rv, GenerateUID());
      } else {
        rv = NULL;
      }
      break;
    case ADBOSKEL:
      skel = aDB->skel;
      if(skel->misc){
        rv = skel->misc(skel->opq, name, args);
      } else {
        rv = NULL;
      }
      break;
    default:
      rv = NULL;
      break;
  }
  return rv;  
}

/* Store a record into a database object with a duplication handler.
   `mul' specifies the multiple database object.
   `kbuf' specifies the pointer to the region of the key.
   `ksiz' specifies the size of the region of the key.
   `vbuf' specifies the pointer to the region of the value.
   `vsiz' specifies the size of the region of the value.
   `proc' specifies the pointer to the callback function to process duplication.
   `op' specifies an arbitrary pointer to be given as a parameter of the callback function.
   If successful, the return value is true, else, it is false. */
/*
static bool tcMeDBputproc(TMeDB *mul, const void *kbuf, int ksiz, const void *vbuf, int vsiz,
                            TCPDPROC proc, void *op){
  assert(mul && kbuf && ksiz >= 0 && proc);
  if(!mul->adbs) return false;
  int idx = tcMeDBidx(mul, kbuf, ksiz);
  TCADB *adb = mul->adbs[idx];
  return tcadbputproc(adb, kbuf, ksiz, vbuf, vsiz, proc, op);
}
*/

// END OF FILE
